#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip_icmp.h>
#include <arpa/inet.h>
#include <unistd.h>

#define ICMP_ECHO_REQUEST 8
#define ICMP_ECHO_REPLY   0
#define PACKET_SIZE       1024

// 计算ICMP校验和
unsigned short cal_checksum(unsigned short *buf, int len) {
    unsigned long sum = 0;
    while (len > 1) {
        sum += *buf++;
        len -= 2;
    }
    if (len == 1) {
        sum += *(unsigned char *)buf;
    }
    sum = (sum >> 16) + (sum & 0xffff);
    sum += (sum >> 16);
    return (unsigned short)(~sum);
}

int main() {
    int sockfd;
    struct sockaddr_in src_addr;
    char packet[PACKET_SIZE];
    int n;
    socklen_t addr_len;

    // 创建原始套接字
    sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
    if (sockfd < 0) {
        perror("socket error");
        exit(1);
    }

    addr_len = sizeof(src_addr);
    while (1) {
        // 接收ICMP请求
        n = recvfrom(sockfd, packet, PACKET_SIZE, 0, (struct sockaddr *)&src_addr, &addr_len);
        if (n < 0) {
            perror("recvfrom error");
            continue;
        }

        struct ip *ip_packet = (struct ip *)packet;
        struct icmp *icmp_packet = (struct icmp *)(packet + (ip_packet->ip_hl << 2));

        // 处理ICMP回显请求
        if (icmp_packet->icmp_type == ICMP_ECHO_REQUEST) {
            icmp_packet->icmp_type = ICMP_ECHO_REPLY;
            icmp_packet->icmp_cksum = 0;
            icmp_packet->icmp_cksum = cal_checksum((unsigned short *)icmp_packet, n - (ip_packet->ip_hl << 2));

            // 发送ICMP应答
            if (sendto(sockfd, packet, n, 0, (struct sockaddr *)&src_addr, addr_len) < 0) {
                perror("sendto error");
            }
        }
    }

    close(sockfd);
    return 0;
}